/*global $, jQuery, window, LOCK5MAIN*/

$(function() {
    // global objects
    LOCK5MAIN.histogramTypes = ["mean", "median", "stdev"];
    LOCK5MAIN.histograms = {};
    $.each(LOCK5MAIN.histogramTypes, function(i, type) {
        LOCK5MAIN.histograms[type] = $.createHistogram();
        LOCK5MAIN.histograms[type].type = type;
        LOCK5MAIN.histograms[type].two_plot = true;
        LOCK5MAIN.histograms[type].two_plot_single = true;
    });
    LOCK5MAIN.activeHistogram = LOCK5MAIN.histograms.mean;
    LOCK5MAIN.hypothesisTest = true;
    
    /* Methods */
    /*
     * LOCK5MAIN.clearGraph Clears everything to do with the LOCK5MAIN
     * randomization graph including data and control settings.
     *
     * Returns true
     */
    LOCK5MAIN.clearGraph = function() {
        $.each(LOCK5MAIN.histograms, function(i, h) {
            h.clear();
        });
        LOCK5MAIN.adjustDotplotBounds();
            
        //LOCK5MAIN.doublePlotProperties = null;
        LOCK5MAIN.dotPlot.clear();
        LOCK5MAIN.singlePlot("#current-sample-plot", []);
        return true;
    };
    /*
     * hoverGraph: Function that is called when the mouse hovers over a
     * point on the dotplot
     * 
     * parameters: id (String) the css id of the element to plot over
     */
    LOCK5MAIN.hoverGraph = function(id) {
        return function(event, pos, item) {
            if (item) {
                var i, generatedFrom = item.series.data[item.dataIndex][2];
                LOCK5MAIN.singlePlot(id, generatedFrom, {labels: LOCK5MAIN.ORIGINAL_SAMPLE.label, useOldAxes: true});
            }
        };
    };
    /*
     * LOCK5MAIN.generateNBootStrapSamples: generates statistics from the given
     * points
     *
     * returns: Array of statistics. A statistic is an object containing
     * statistics for each input function type as well as the bootstrap sample
     * that generated it
     *
     * parameters: point (Array): the original data to sample n (Integer):
     * nubmer of points to generate 
     * statFunction (function): calculates the statistic
     */
    LOCK5MAIN.generateNBootStrapSamples = function(points, n, statfunction) {
        var i, index, pointNumber, sampleNumber, randValue, tmpPoints, statistics = [],
        meanFn, stdevFn, deltaMean; 
        meanFn = function(array) {
            var mean = 0, i;
            for (i = 0; i < array.length; i+= 1) {
                mean += array[i];
            }
            return mean/array.length;
        };
        stdevFn = function(arr) {
            if (arr.length <= 1) {
                return NaN;
            }
            var sum = 0, mean = meanFn(arr), i;
            for (i = 0; i < arr.length; i+= 1) {
                sum += Math.pow(arr[i] - mean, 2);
            }
            return Math.sqrt(sum / (arr.length - 1));
        };
        
        deltaMean = LOCK5MAIN.nullHypothesis-$.mean(points,0);
        
        // generate n bootstrap samples
        for (sampleNumber = 0; sampleNumber < n; sampleNumber += 1) {
            tmpPoints=[];
            for (i = 0; i < points.length; i+= 1) {
                index = Math.floor(Math.random()*points.length);
                tmpPoints.push(points[index][0]+deltaMean);
            }
            statistics.push({
                mean: meanFn(tmpPoints),
                median: $.median(tmpPoints),
                stdev: stdevFn(tmpPoints),
                generatedFrom: tmpPoints
            });
        }
        return statistics;
    };
    /*
     * generateAndPlotBootstrapSample: helper function to generate n additional
     * points and plot the current total.
     *
     * returns: true
     *
     * parameters: point (Array): the original data to sample n (Integer):
     * nubmer of points to generate slopeFunction (function): calculates the
     * slope statistic from the boostrap points correlationFunction (function):
     * calculates the correlation statistic from the boostrap points
     */
    LOCK5MAIN.generateAndPlotBootstrapSample = function(n) { //
        return function() {
        
            // calculate samples
            var statistics = LOCK5MAIN.generateNBootStrapSamples(LOCK5MAIN.ORIGINAL_SAMPLE.data, n, $.differenceInMeans);
            
            // add those samples to each histogram
            $.each(LOCK5MAIN.histograms, function(i, h) {
                h.addPoints(statistics);
            });
            // plot the bootstrap plot with the old and new samples from
            // the current active histogram
            LOCK5MAIN.dotPlot.plot("#dotplot", LOCK5MAIN.activeHistogram.histogramPoints);
            
            // Plot the most recent statistic in the hoverplot so it animates
            // as single stats are added at a time. Doesn't make sense if
            // many points are added, but still keep it for uniformity
            LOCK5MAIN.singlePlot("#current-sample-plot", statistics[0].generatedFrom, {labels: LOCK5MAIN.ORIGINAL_SAMPLE.label, useOldAxes: true});
            
            return true;
        };
    };
    
    // bootstrap some initial points to determine the initial xaxis
    // bounds on graph
    LOCK5MAIN.adjustDotplotBounds = function() {
        samplePoints = LOCK5MAIN.generateNBootStrapSamples(LOCK5MAIN.ORIGINAL_SAMPLE.data, 1000, $.differenceInMeans);
        
        // set up ranges on each histogram
        $.each(LOCK5MAIN.histograms, function(type, h) {
            (function(array) {
                h.max = $.selection(array, 0);
                h.min = $.selection(array, array.length - 1);
            }($.map(samplePoints, function(sample) {
                return sample[type];
            })));
        });
        LOCK5MAIN.ORIGINAL_BOUNDS = {
            xaxis: {
                //histogram sorts points backwards
                min: LOCK5MAIN.activeHistogram.max,
                max: LOCK5MAIN.activeHistogram.min
            },
            yaxis: {
                min: 0,
                max: Math.floor(parseInt($("#dotplot").height(), 10) / LOCK5MAIN.PIXELSPERDOT)
            }
        };
        LOCK5MAIN.dotPlot.setBounds({
            xaxis: LOCK5MAIN.ORIGINAL_BOUNDS.xaxis,
            yaxis: LOCK5MAIN.ORIGINAL_BOUNDS.yaxis});
    };
    
    /*
     * LOCK5MAIN.initialize(data,domain,range) Initializes both plots based on
     * the 2D set of points passed to it.
     *
     * returns None
     *
     * Parameters data (Array): an N dimensional array made up 2D arrays
     * representing original sample points domain (String): the label of the
     * domain range (String): the label of the range label (String): the label
     * of the data to display to a user
     */
    LOCK5MAIN.initialize = function(data, variable, unused, label, dataset) {
        var bootstrapGraphWidth, bootstrapGraphHeight, samplePoints;
        
        if (dataset && dataset.nullmean) {
            LOCK5MAIN.nullHypothesis = dataset.nullmean;
        }
        
        LOCK5MAIN.ORIGINAL_SAMPLE.data = data;
        LOCK5MAIN.ORIGINAL_SAMPLE.variable = variable;
        LOCK5MAIN.ORIGINAL_SAMPLE.label = label;

        //replace default hover function on smaller plots
        $("#original-sample-plot").unbind("plothover", LOCK5MAIN.hoverPoint);
        $("#current-sample-plot").unbind("plothover", LOCK5MAIN.hoverPoint);
        $("#original-sample-plot").bind("plothover", LOCK5MAIN.hoverXValue);
        $("#current-sample-plot").bind("plothover", LOCK5MAIN.hoverXValue);
        
        // LOCK5MAIN.PIXELSPERDOT and physical width of graph determine
        // histogram buckets
        bootstrapGraphHeight = parseInt($("#dotplot").height(), 10);
        bootstrapGraphWidth = parseInt($("#dotplot").width(), 10);
        
        LOCK5MAIN.adjustDotplotBounds();
        
        $.each(LOCK5MAIN.histograms, function(key, histogram) {
            histogram.k = Math.floor(bootstrapGraphWidth / LOCK5MAIN.PIXELSPERDOT);
        });
        LOCK5MAIN.singlePlot("#original-sample-plot", LOCK5MAIN.ORIGINAL_SAMPLE.data, {
            labels: LOCK5MAIN.ORIGINAL_SAMPLE.label
        });
        
        LOCK5MAIN.dotPlot.plot("#dotplot", []);
        
        LOCK5MAIN.singlePlot("#current-sample-plot", []);
        LOCK5MAIN.updateNullHypothesisControl();
        LOCK5MAIN.clearGraph();
        
    };
    /** nullHypothesisDialog: creates the null hypothesis dialog box that's used to change
     * the null hypothesis
     */
    $( function nullHypothesisDialog() {
        var nullHypothesisForm = $('<div id="null-hypothesis-form"><p class="validateTips">Enter the null hypothesis mean.<\/p><form><label for="null-hypothesis-input">Null Hypothesis<\/label><input type="text" name="null-hypothesis-input" id="null-hypothesis-input" class="text ui-widget-content ui-corner-all"\/><\/form><\/div>'),
        callback = function(dialog) {
            return function() {
                var nullHypothesis, valid;
                nullHypothesis = parseFloat($("#null-hypothesis-input", dialog).val());
                valid = !isNaN(nullHypothesis);
                // validate
                if (!valid) {
                    $("#cutoff", dialog).addClass("ui-state-error");
                    $(".validateTips", dialog).addClass("ui-state-highlight");
                } else {
                    LOCK5MAIN.nullHypothesis = nullHypothesis;
                    dialog.dialog("close");
                    LOCK5MAIN.updateNullHypothesisControl();
                    LOCK5MAIN.clearGraph();
                }
                return false;
            };
        };
        nullHypothesisForm.dialog({
            autoOpen: false,
            title: "Define Null Hypothesis",
            modal: true,
            show: {
                effect: "fade",
                speed: "fast"
            },
            hide: {
                effect: "fade",
                speed: "fast"
            },
            buttons: [{
                text: "Ok (or hit Enter)",
                click: callback(nullHypothesisForm)
            }]
        });
        nullHypothesisForm.submit(callback(nullHypothesisForm));
    });
    /**
     * updateHypothesisCallback: funciton used when the null hypothesis needs
     * to be updated
     */
    LOCK5MAIN.updateHypothesisCallback = ( function() {
        return function() {
            $("#null-hypothesis-form").dialog("open");
        };
    }());
    /**
     * updateNullHypothesisControl: this function serves as a central point to
     * update the null hypothesis control on the main application
     */
    LOCK5MAIN.updateNullHypothesisControl = function() {
        //Display the message and provide the callback.
        $("#null-hypothesis").html("<em>$\\mu = $"+"<button id='null-hypothesis-button'><\/button><\/em>");
        MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
        $("#null-hypothesis-button").button({
            label: LOCK5MAIN.nullHypothesis
        }).click(LOCK5MAIN.updateHypothesisCallback);
    };
    /* Initialize the application*/
    $(function() {
        var callback = function(histogram) {
            return function() {
                LOCK5MAIN.activeHistogram = histogram;
                LOCK5MAIN.dotPlot.setBounds({
                    xaxis: {
                        //histogram sorts points backwards
                        min: LOCK5MAIN.activeHistogram.max,
                        max: LOCK5MAIN.activeHistogram.min
                    },
                    yaxis: {
                        min: 0,
                        max: Math.floor(parseInt($("#dotplot").height(), 10) / LOCK5MAIN.PIXELSPERDOT)
                    }
                });
                LOCK5MAIN.dotPlot.plot("#dotplot", LOCK5MAIN.activeHistogram.histogramPoints, LOCK5MAIN.activeHistogram.graphProperties);
            };
        };
        
        LOCK5MAIN.startup({
            sampleType: "Bootstrap",
            generateAndPlot: LOCK5MAIN.generateAndPlotBootstrapSample,
            graphAnnotation: function(arrowAnnotationFn, plot, xyList) {
                var meanX = $.mean(xyList, 3), meanText = Math.truncate(meanX, 3);
                arrowAnnotationFn(plot, meanX, meanText, "#000");
            },onLoad: function () {
                LOCK5MAIN.updateNullHypothesisControl();
            }
        });
    });
});
